How can network data look like
Data collection vs. analyses
Visualization of bipartite networks
Linked notes are on the same level/plane
Notes are belonging to two levels/planes
Notes are belonging to multiple levels/planes
We can visualize bipartite networks as:
library(bipartite)
## Loading required package: vegan
## Loading required package: permute
## Loading required package: lattice
## This is vegan 2.6-4
## Loading required package: sna
## Loading required package: statnet.common
##
## Attaching package: 'statnet.common'
## The following objects are masked from 'package:base':
##
## attr, order
## Loading required package: network
##
## 'network' 1.18.1 (2023-01-24), part of the Statnet Project
## * 'news(package="network")' for changes since last version
## * 'citation("network")' for citation information
## * 'https://statnet.org' for help, support, and other information
## sna: Tools for Social Network Analysis
## Version 2.7-1 created on 2023-01-24.
## copyright (c) 2005, Carter T. Butts, University of California-Irvine
## For citation information, type citation("sna").
## Type help(package="sna") to get started.
## This is bipartite 2.18.
## For latest changes see versionlog in ?"bipartite-package". For citation see: citation("bipartite").
## Have a nice time plotting and analysing two-mode networks.
##
## Attaching package: 'bipartite'
## The following object is masked from 'package:vegan':
##
## nullmodel
plotweb(Safariland)
visweb(Safariland)
= Vázquez et al. (2009)
What packages are we going to use?
#library(bipartite) # mainly for network analyses
How we usually collect data in field:
| Time | Plot | Plant | Pollinator | Number of interactions |
|---|---|---|---|---|
| 23:30 | 1 | Cen_jac | Eri_ten | 10 |
| 23:45 | 2 | Chuq_jus | Col_ibr | 1 |
| 23:45 | 2 | Cen_jac | Eri_ten | 5 |
But we usually want something like this:
| Cen_jac | Chuq_jus | |
|---|---|---|
| Eri_ten | 15 | 0 |
| Col_ibr | 0 | 1 |
How to transfer them?
HairEye <- data.frame(HairEyeColor) # we will do a little bit of cheating here. We will use HairEyeColor and put them to the dataframe
HairEye # This is how data usually looks like when obtained in the field
## Hair Eye Sex Freq
## 1 Black Brown Male 32
## 2 Brown Brown Male 53
## 3 Red Brown Male 10
## 4 Blond Brown Male 3
## 5 Black Blue Male 11
## 6 Brown Blue Male 50
## 7 Red Blue Male 10
## 8 Blond Blue Male 30
## 9 Black Hazel Male 10
## 10 Brown Hazel Male 25
## 11 Red Hazel Male 7
## 12 Blond Hazel Male 5
## 13 Black Green Male 3
## 14 Brown Green Male 15
## 15 Red Green Male 7
## 16 Blond Green Male 8
## 17 Black Brown Female 36
## 18 Brown Brown Female 66
## 19 Red Brown Female 16
## 20 Blond Brown Female 4
## 21 Black Blue Female 9
## 22 Brown Blue Female 34
## 23 Red Blue Female 7
## 24 Blond Blue Female 64
## 25 Black Hazel Female 5
## 26 Brown Hazel Female 29
## 27 Red Hazel Female 7
## 28 Blond Hazel Female 5
## 29 Black Green Female 2
## 30 Brown Green Female 14
## 31 Red Green Female 7
## 32 Blond Green Female 8
## This is often called "long" data
## How to translate it into "wide" data?
HairEyeweb <- tapply(HairEye$Freq, list(HairEye$Hair,HairEye$Eye), sum) # simply by tapply() function
HairEyeweb
## Brown Blue Hazel Green
## Black 68 20 15 5
## Brown 119 84 54 29
## Red 26 17 14 14
## Blond 7 94 10 16
plotweb(HairEyeweb) # ploting the web
plotweb(HairEyeweb) # ploting the web
plotweb(HairEyeweb,
method="cca", # Default method is cca, which leads to as few crossings of interactions as possible. The other option is normal, which leaves order as given by the matrix.
text.rot = 90,
labsize =1,
ybig = 0.8,
low.y = 0.6,
high.y = 0.98,
plot.axes = FALSE,
y.width.low = 0.05,
y.width.high = 0.05,
low.spacing=0.01,
high.spacing=0.01,
bor.col.low = "black",
col.low = "red",
col.high = "gray",
bor.col.high = "black",
bor.col.interaction = rgb(250,250,250, alpha = 50, max=255),
col.interaction=rgb(0,0,0, alpha = 90, max=255))
visweb(HairEyeweb, labsize = 0.5 )
gplot(as.one.mode(HairEyeweb, project="lower"),
label=rownames(HairEyeweb), gmode="graph",
label.cex=0.6, vertex.cex=2, vertex.col="red")
Safariland
## Policana albopilosa Bombus dahlbomii
## Aristotelia chilensis 673 0
## Alstroemeria aurea 0 154
## Schinus patagonicus 0 0
## Berberis darwinii 0 67
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Ruizantheda mutabilis Trichophthalma amoena
## Aristotelia chilensis 110 0
## Alstroemeria aurea 0 0
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 6 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 2
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Syrphus octomaculatus Manuelia gayi
## Aristotelia chilensis 0 0
## Alstroemeria aurea 5 7
## Schinus patagonicus 0 0
## Berberis darwinii 5 0
## Rosa eglanteria 4 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Allograpta.Toxomerus Trichophthalma jaffueli Phthiria
## Aristotelia chilensis 0 0 0
## Alstroemeria aurea 1 3 8
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 2 0 0
## Cynanchum diemii 0 0 0
## Ribes magellanicum 3 0 0
## Mutisia decurrens 0 0 1
## Calceolaria crenatiflora 1 0 0
## Platycheirus1 Sapromyza.Minettia Formicidae3
## Aristotelia chilensis 4 0 0
## Alstroemeria aurea 1 1 0
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 0 0 0
## Cynanchum diemii 0 0 8
## Ribes magellanicum 0 0 0
## Mutisia decurrens 0 0 0
## Calceolaria crenatiflora 0 0 0
## Nitidulidae Staphilinidae Ichneumonidae4 Braconidae3
## Aristotelia chilensis 0 0 1 0
## Alstroemeria aurea 0 4 0 0
## Schinus patagonicus 0 0 15 0
## Berberis darwinii 0 0 0 0
## Rosa eglanteria 0 3 0 0
## Cynanchum diemii 1 0 0 2
## Ribes magellanicum 0 0 0 0
## Mutisia decurrens 0 0 0 0
## Calceolaria crenatiflora 0 0 0 0
## Chalepogenus caeruleus Vespula germanica Torymidae2
## Aristotelia chilensis 0 0 0
## Alstroemeria aurea 0 4 0
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 0 0 0
## Cynanchum diemii 0 0 9
## Ribes magellanicum 0 0 0
## Mutisia decurrens 0 0 0
## Calceolaria crenatiflora 3 0 0
## Phthiria1 Svastrides melanura Sphecidae Thomisidae
## Aristotelia chilensis 0 0 0 0
## Alstroemeria aurea 1 6 1 1
## Schinus patagonicus 0 0 0 0
## Berberis darwinii 0 0 0 0
## Rosa eglanteria 0 0 0 0
## Cynanchum diemii 0 0 0 0
## Ribes magellanicum 0 0 0 0
## Mutisia decurrens 0 0 0 0
## Calceolaria crenatiflora 0 0 0 0
## Corynura prothysteres Ichneumonidae2
## Aristotelia chilensis 1 0
## Alstroemeria aurea 3 4
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Ruizantheda proxima Braconidae2
## Aristotelia chilensis 0 1
## Alstroemeria aurea 4 0
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
Safariland
## Policana albopilosa Bombus dahlbomii
## Aristotelia chilensis 673 0
## Alstroemeria aurea 0 154
## Schinus patagonicus 0 0
## Berberis darwinii 0 67
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Ruizantheda mutabilis Trichophthalma amoena
## Aristotelia chilensis 110 0
## Alstroemeria aurea 0 0
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 6 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 2
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Syrphus octomaculatus Manuelia gayi
## Aristotelia chilensis 0 0
## Alstroemeria aurea 5 7
## Schinus patagonicus 0 0
## Berberis darwinii 5 0
## Rosa eglanteria 4 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Allograpta.Toxomerus Trichophthalma jaffueli Phthiria
## Aristotelia chilensis 0 0 0
## Alstroemeria aurea 1 3 8
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 2 0 0
## Cynanchum diemii 0 0 0
## Ribes magellanicum 3 0 0
## Mutisia decurrens 0 0 1
## Calceolaria crenatiflora 1 0 0
## Platycheirus1 Sapromyza.Minettia Formicidae3
## Aristotelia chilensis 4 0 0
## Alstroemeria aurea 1 1 0
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 0 0 0
## Cynanchum diemii 0 0 8
## Ribes magellanicum 0 0 0
## Mutisia decurrens 0 0 0
## Calceolaria crenatiflora 0 0 0
## Nitidulidae Staphilinidae Ichneumonidae4 Braconidae3
## Aristotelia chilensis 0 0 1 0
## Alstroemeria aurea 0 4 0 0
## Schinus patagonicus 0 0 15 0
## Berberis darwinii 0 0 0 0
## Rosa eglanteria 0 3 0 0
## Cynanchum diemii 1 0 0 2
## Ribes magellanicum 0 0 0 0
## Mutisia decurrens 0 0 0 0
## Calceolaria crenatiflora 0 0 0 0
## Chalepogenus caeruleus Vespula germanica Torymidae2
## Aristotelia chilensis 0 0 0
## Alstroemeria aurea 0 4 0
## Schinus patagonicus 0 0 0
## Berberis darwinii 0 0 0
## Rosa eglanteria 0 0 0
## Cynanchum diemii 0 0 9
## Ribes magellanicum 0 0 0
## Mutisia decurrens 0 0 0
## Calceolaria crenatiflora 3 0 0
## Phthiria1 Svastrides melanura Sphecidae Thomisidae
## Aristotelia chilensis 0 0 0 0
## Alstroemeria aurea 1 6 1 1
## Schinus patagonicus 0 0 0 0
## Berberis darwinii 0 0 0 0
## Rosa eglanteria 0 0 0 0
## Cynanchum diemii 0 0 0 0
## Ribes magellanicum 0 0 0 0
## Mutisia decurrens 0 0 0 0
## Calceolaria crenatiflora 0 0 0 0
## Corynura prothysteres Ichneumonidae2
## Aristotelia chilensis 1 0
## Alstroemeria aurea 3 4
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
## Ruizantheda proxima Braconidae2
## Aristotelia chilensis 0 1
## Alstroemeria aurea 4 0
## Schinus patagonicus 0 0
## Berberis darwinii 0 0
## Rosa eglanteria 0 0
## Cynanchum diemii 0 0
## Ribes magellanicum 0 0
## Mutisia decurrens 0 0
## Calceolaria crenatiflora 0 0
plotweb(Safariland, method = "cca")
visweb(Safariland)
Just a note: Sometimes it can be useful to change the network to binary network. For that we have function network2bin()
#library(bipartite)
#Safariland2 <- network2bin(Safariland) # simply cahnge all the numbers higher than 0 to 1
#visweb(Safariland) # non-binary web
#visweb(Safariland2) # binary web
jc_net <- read.csv("jc_network21.csv")
# Or from Github directly
#jc_net <- read.csv("https://raw.githubusercontent.com/lycanea/network_course/main/jc_network21.csv?token=GHSAT0AAAAAACO5AVK36PCO3JJULQZ3K37CZPJ6PCA")
head(jc_net)
## pollinator day hour min plot plant number sex shade note is.pollinator
## 1 Aglais_urticae 14 16 50 39 Bet_off 1 <NA> 0 <NA> T
## 2 Aglais_urticae 14 9 28 36 Cen_jac 1 <NA> 0 <NA> T
## 3 Aglais_urticae 14 15 58 9 Suc_pra 1 <NA> 0 <NA> T
## 4 Aglais_urticae 14 13 39 41 Tri_pra 1 <NA> 0 <NA> T
## 5 Aglais_urticae 15 13 34 9 Suc_pra 1 <NA> 0 <NA> T
## 6 Aglais_urticae 14 17 37 9 Suc_pra 1 <NA> 0 <NA> T
#jc_net <- read.csv("jc_network21.csv")
head(jc_net)
## pollinator day hour min plot plant number sex shade note is.pollinator
## 1 Aglais_urticae 14 16 50 39 Bet_off 1 <NA> 0 <NA> T
## 2 Aglais_urticae 14 9 28 36 Cen_jac 1 <NA> 0 <NA> T
## 3 Aglais_urticae 14 15 58 9 Suc_pra 1 <NA> 0 <NA> T
## 4 Aglais_urticae 14 13 39 41 Tri_pra 1 <NA> 0 <NA> T
## 5 Aglais_urticae 15 13 34 9 Suc_pra 1 <NA> 0 <NA> T
## 6 Aglais_urticae 14 17 37 9 Suc_pra 1 <NA> 0 <NA> T
jc_web <- tapply(jc_net$number , list(jc_net$pollinator, jc_net$plant), sum) #
jc_web[is.na(jc_web)] <- 0 # replace NA with 0
plotweb(jc_web, "cca")
plotweb(jc_web, "cca", col.interaction = rgb(0,0,0, max = 255, alpha = 100), bor.col.interaction = rgb(0,0,0, max = 255, alpha = 100))
library(networkD3)
## Warning: package 'networkD3' was built under R version 4.2.3
# Load energy projection data
URL <- "https://cdn.rawgit.com/christophergandrud/networkD3/master/JSONdata/energy.json"
Energy <- jsonlite::fromJSON(URL)
# Now we have 2 data frames: a 'links' data frame with 3 columns (from, to, value), and a 'nodes' data frame that gives the name of each node.
head( Energy$links )
## source target value
## 1 0 1 124.729
## 2 1 2 0.597
## 3 1 3 26.862
## 4 1 4 280.322
## 5 1 5 81.144
## 6 6 2 35.000
head( Energy$nodes )
## name
## 1 Agricultural 'waste'
## 2 Bio-conversion
## 3 Liquid
## 4 Losses
## 5 Solid
## 6 Gas
# Thus we can plot it
p <- sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30)
p
# save the widget
# library(htmlwidgets)
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/sankeyEnergy.html"))
library(networkD3)
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.2.3
## Warning: package 'ggplot2' was built under R version 4.2.3
## Warning: package 'tibble' was built under R version 4.2.3
## Warning: package 'tidyr' was built under R version 4.2.3
## Warning: package 'readr' was built under R version 4.2.3
## Warning: package 'purrr' was built under R version 4.2.3
## Warning: package 'dplyr' was built under R version 4.2.3
## Warning: package 'forcats' was built under R version 4.2.3
## Warning: package 'lubridate' was built under R version 4.2.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.2 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ ggplot2 3.4.3 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.0
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ lubridate::%--%() masks igraph::%--%()
## ✖ dplyr::as_data_frame() masks tibble::as_data_frame(), igraph::as_data_frame()
## ✖ purrr::compose() masks igraph::compose()
## ✖ tidyr::crossing() masks igraph::crossing()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ purrr::simplify() masks igraph::simplify()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# I need a long format
data_long <- data.frame(jc_web) %>%
rownames_to_column %>%
gather(key = 'key', value = 'value', -rowname) %>%
filter(value > 0)
colnames(data_long) <- c("source", "target", "value")
data_long$target <- paste(data_long$target, " ", sep="")
# From these flows we need to create a node data frame: it lists every entities involved in the flow
nodes <- data.frame(name=c(as.character(data_long$source), as.character(data_long$target)) %>% unique())
# With networkD3, connection must be provided using id, not using real name like in the links dataframe.. So we need to reformat it.
data_long$IDsource=match(data_long$source, nodes$name)-1
data_long$IDtarget=match(data_long$target, nodes$name)-1
# prepare colour scale
# Make the Network
sankeyNetwork(Links = data_long, Nodes = nodes,
Source = "IDsource", Target = "IDtarget",
Value = "value", NodeID = "name",
sinksRight=FALSE, nodeWidth=40, fontSize=13, nodePadding=20)
Network of US airports and domestic flights